R DATA VISUALIZATION
Raw data point doesn’t provide much insight to kick off data
analysis.
Data Visualization is brilliant in - exploring the pattern of data
briefly at the early stage - in the final conclusion, enhance the story
telling of data analysis (inforgraphics play huge role for this
purpose)
Built-in Plot Functions
The advantage of using built-in plotting utilities is they are easy.
Let you quickly visually the data pattern while you are trying to gain
some brief insight and prepare for your models.

Grammar of Graphics: ggplot2
If these built-in plotting tools are not enough for you, go fo
ggplot2
the most popular data visualization for R.
ggplot2 is an open-source data visualization package for R. A data
visualization which breaks up graphs into semantic components such as
scales and layers. Since 2005, ggplot2 has grown in use to become one of
the most popular R packages.
BASIC GRAMMAR
ggplot2 is based on the grammar of graphics, the idea that you can
build every graph from the same components: a data set
+ a coordinate system + and geoms—visual marks
that represent data points
Use Built-in Datasets
Let’s use the built-in cars data set
print(cars)

geom_point() function
It’s easy to add geometry layer to the base co-ordinate
Let’s add a layer of data points.
Yes, you can add layer by using the + operator Let’s use point (namely
geom_point()). In 2D co-ordinate, a point is describe
by its x and y value.
We need to provide a mapping that specifies the data columns’ name to
map to the x and y value of a point
That mapping is defined by an aesthetics function
aes()
Scatterplot is useful to explore the relation of two variable.
cars %>% ggplot() +
geom_point(mapping = aes(x=speed, y=dist))

Use geom_line() to replace geom_point()
geom_point() and geom_line() require very similar parameters.
geom_line() is simply an enhanced visualization that automatically
connect all the points

Use geom_smoth() to project a smooth line
again geom_smooth() and geom_point() require very similar
parameters.
geom_smooth() smooths out the line progression
cars %>% ggplot() +
geom_smooth(mapping = aes(x=speed, y=dist))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Adding Aesthetics to your Plots
cars %>% ggplot() +
geom_point(mapping = aes(x=speed, y=dist),
color = "orange", # the color of data points
# size = 3, # the size of data point
# alpha = 0.5, # the transparency of data points, min=0, max=1
# shape = 0, # the shape of data point
)

PLOT WITH OUR OWN DATA
Loading Data: allowance & graduates
allowance = read_csv("./data/allowance.csv")
Rows: 11 Columns: 13
── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): Assessment_Year, Personal_Disability_Allowance
dbl (11): Basic, Married_Person, Child, Child_newborn, Dependent_Brother_Sister, Dependent_Parent_60, Dep...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(allowance)
Allowance Data Set in Simple Scatterplot

Continuous Values vs. Discrete Values
Continuous values refer to numbers value that has wide range Discrete
values refer to a limited number of valid values. It can be string. It
can be a few distinct numbers.
When you produce plots, pay attention to what type of value are
required by the geoms.
In many case, you will need to convert the data first.
mutate() function are quite often used for that.
example:
allowance = allowance %>%
mutate(Assessment_Year = as.numeric(substr(Assessment_Year, 1 ,4)))
Simple Line Plot

Adding Multiple Layers of Geometry

Use geom_smooth() to smooth out the line
allowance %>% ggplot(aes(x=Assessment_Year, y=Basic, group=1, color="Orange")) +
geom_smooth() +
geom_point(size=5)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Save a Plot: ggsave()
my.first.plot = allowance %>%
ggplot() +
geom_point(
mapping=aes(x=Assessment_Year, y=Basic),
color = "orange",
size = 3,
)
print(my.first.plot)
ggsave("./output/my_first_plot.png") # default image size
Saving 7.29 x 4.51 in image
ggsave("./output/my_first_plot_large.png", width=10, height=10)

Bar Chart with geom_col()
allowance %>%
ggplot() +
geom_col(mapping=aes(x=Assessment_Year, y=Basic),
fill="tomato")

Histogram with geom_bar()
Counting the frequency of each occurrence of observed value.

WORK WITH MORE COMPLEX DATA
Loading Data: graduates.csv
graduates = read_csv("./data/graduates.csv")
Rows: 601 Columns: 5
── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): AcademicYear, LevelOfStudy, ProgrammeCategory, Sex
dbl (1): Headcount
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(graduates)
Simple Scatterplot
Let’s explore the data with some simple ggplot plot. Overall they are
not very useful. Just some quick exploration.
ggplot(data=graduates) +
geom_point(mapping=aes(x=AcademicYear, y=Headcount))

ggplot(data=graduates) +
geom_point(mapping=aes(x=AcademicYear, y=Headcount, shape=Sex)
)

ggplot(data=graduates) +
geom_point(mapping=aes(x=AcademicYear, y=Headcount, color=Sex)
)

Use Line Plot To Explore the Trending
Use line plot to explore the trending of “Business and Management”
student headcount trending
library(magrittr)
graduates %<>%
mutate(AcademicYear=as.factor(AcademicYear),
Sex=as.factor(Sex)
) # convert the AcademicYear and Sex to factor type
graduates %>%
filter(LevelOfStudy=="Undergraduate", ProgrammeCategory=="Business and Management") %>%
ggplot() +
geom_line(
mapping=aes(x=AcademicYear,
y=Headcount,
group=Sex,
color=Sex
)
)

GROUPING AND AGGREGATION
Using group_by() and summarise()
graduates %>% group_by(AcademicYear, LevelOfStudy) %>%
summarise(TotalHeadcount = sum(Headcount))
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.
graduates %>% group_by(AcademicYear, LevelOfStudy) %>%
summarise(TotalHeadcount = sum(Headcount)) %>%
ggplot(
aes(x=AcademicYear,
y=TotalHeadcount,
group=LevelOfStudy,
color=LevelOfStudy
)
) +
geom_line() +
geom_point()
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.

NA
Use of filter()
Use filter() to keep only “Taught Postgraduate” Records
This plot is not very useful without previously applying filter() and
group_by() and summarise()

filter() + group_by() + summarise()
Use filter() to extract required rows Use group_by() and summarise()
to group and aggreate total headcout for both male and female
graduates %>%
filter(LevelOfStudy=="Undergraduate") %>%
group_by(AcademicYear, ProgrammeCategory) %>%
summarise(TotalHeadcount = sum(Headcount)) %>%
ggplot() +
geom_line(mapping=aes(x=AcademicYear,y=TotalHeadcount, group=ProgrammeCategory, color=ProgrammeCategory))
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.

geom_col() function


More Aggregation Functions
Center: mean(), median() Spread: sd(), IQR(), mad() Range: min(),
max(), quantile() Position: first(), last(), nth(), Count: n(),
n_distinct() Logical: any(), all()
More information at summarise()
function
geom_bar() function
bar chart give the counting frequency (number of record in the data
set)

box plot
The boxplot compactly displays the distribution of a continuous
variable. It visualises five summary statistics (the median, two hinges
and two whiskers), and all “outlying” points individually.
graduates %>%
ggplot() +
geom_point(mapping=aes(x=Sex, y=Headcount))
graduates %>%
ggplot() +
geom_boxplot(mapping=aes(x=LevelOfStudy, y=Headcount))
MAKE IT PRETTY
Use of title, label, background color and themes

Plot Background
level.bar.plot # default style

level.bar.plot +
theme(plot.background = element_rect(fill="orange"))

Panel Background
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_rect(fill="orange")) # styling the panel background

Remove Plot and Panel Background
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) +
theme(plot.background = element_blank()) +
theme(panel.grid.major.y = element_line(color="grey"))

Label
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") # Label for X axis

Change Fill Colors
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"))

Styling Legends
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
)

NA
Title
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
) + # move legend position to top and label position to bottom
ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019")

Adding Annotations Text
Add extra texts/shape to enhance your visualization
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
) + # move legend position to top and label position to bottom
ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019") +
annotate("text", label="Record\nHigh", x="2017/18", y=5300) # you can change value of x and y to set the text position

Adding Reference Line
level.bar.plot # default style

level.bar.plot +
theme(panel.background = element_blank()) + # styling the panel background to none
theme(plot.background = element_blank()) + # styling the plot background to none
theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
ylab("Number of Student") + # Label for Y axis
xlab("Year") + # Label for X axis
theme(legend.position="top") +
scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
guide = guide_legend(title="Level of Study",
label.position = "bottom")
) + # move legend position to top and label position to bottom
ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019") +
annotate("text", label="Record\nHigh", x="2017/18", y=5300) + # you can change text position value of x and y to set the text position
geom_hline(yintercept=3200) + # adds horizontal line
geom_vline(xintercept = "2017/18") # adds vertical line

NA
Using Themes
level.bar.plot # default style

level.bar.plot +
theme_bw() # black and white theme

level.bar.plot +
theme_minimal() # black and white theme

level.bar.plot +
theme_dark() # black and white theme

More 3rd-party Themes
Install ggthemes package to unlock wider selections
of themes.
if (!require("pacman")) install.packages("pacman") # check if pacman already installed. If not, install it.
pacman::p_load(ggthemes)
level.bar.plot # default style

level.bar.plot +
theme_excel() # Excel Theme

level.bar.plot +
theme_wsj() # Wall Street Journal Theme

level.bar.plot +
theme_economist() # Economist Theme

level.bar.plot +
theme_fivethirtyeight() # Wall Street Journal Theme

LS0tCnRpdGxlOiAiUiBJbnRlcm1lZGlhdGUgLSBEYXkgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBSIERBVEEgVklTVUFMSVpBVElPTgoKUmF3IGRhdGEgcG9pbnQgZG9lc24ndCBwcm92aWRlIG11Y2ggaW5zaWdodCB0byBraWNrIG9mZiBkYXRhIGFuYWx5c2lzLlwKCkRhdGEgVmlzdWFsaXphdGlvbiBpcyBicmlsbGlhbnQgaW4KLSBleHBsb3JpbmcgdGhlIHBhdHRlcm4gb2YgZGF0YSBicmllZmx5IGF0IHRoZSBlYXJseSBzdGFnZQotIGluIHRoZSBmaW5hbCBjb25jbHVzaW9uLCBlbmhhbmNlIHRoZSBzdG9yeSB0ZWxsaW5nIG9mIGRhdGEgYW5hbHlzaXMgKGluZm9yZ3JhcGhpY3MgcGxheSBodWdlIHJvbGUgZm9yIHRoaXMgcHVycG9zZSkKCiMjIEJ1aWx0LWluIFBsb3QgRnVuY3Rpb25zClRoZSBhZHZhbnRhZ2Ugb2YgdXNpbmcgYnVpbHQtaW4gcGxvdHRpbmcgdXRpbGl0aWVzIGlzIHRoZXkgYXJlIGVhc3kuCkxldCB5b3UgcXVpY2tseSB2aXN1YWxseSB0aGUgZGF0YSBwYXR0ZXJuIHdoaWxlIHlvdSBhcmUgdHJ5aW5nIHRvIGdhaW4gc29tZSBicmllZiBpbnNpZ2h0IGFuZCBwcmVwYXJlIGZvciB5b3VyIG1vZGVscy4KYGBge3J9CnBsb3QoaXJpcykKYGBgCgojIyBCdWlsdC1pbiBQbG90IFRvb2xzCkZvciBidWlsdC1pbiBkYXRhIHZpc3VhbGl6YXRpb24sIGdvIHRvIHRoZSBSIFByb2dyYW1taW5nIEludHJvIHByb2plY3Qgb24gR2l0aHViIHRvIHJlZnJlc2ggeW91ciBtZW1vcnkKKFIgSW50cm8gU291cmNlIENvZGVzKVtodHRwczovL2dpdGh1Yi5jb20vbmdzYW5sdWsvUi1JbnRyb10KCgojIyBHcmFtbWFyIG9mIEdyYXBoaWNzOiBnZ3Bsb3QyCgpJZiB0aGVzZSBidWlsdC1pbiBwbG90dGluZyB0b29scyBhcmUgbm90IGVub3VnaCBmb3IgeW91LFwgCmdvIGZvICoqZ2dwbG90MioqXAp0aGUgbW9zdCBwb3B1bGFyIGRhdGEgdmlzdWFsaXphdGlvbiBmb3IgUi4KCmdncGxvdDIgaXMgYW4gb3Blbi1zb3VyY2UgZGF0YSB2aXN1YWxpemF0aW9uIHBhY2thZ2UgZm9yIFIuIEEgZGF0YSB2aXN1YWxpemF0aW9uIHdoaWNoIGJyZWFrcyB1cCBncmFwaHMgaW50byBzZW1hbnRpYyBjb21wb25lbnRzIHN1Y2ggYXMgc2NhbGVzIGFuZCBsYXllcnMuIFNpbmNlIDIwMDUsIGdncGxvdDIgaGFzIGdyb3duIGluIHVzZSB0byBiZWNvbWUgb25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgUiBwYWNrYWdlcy4KCgojIyBnZ3Bsb3QyIENoZWF0IFNoZWV0CgpbZ2dwbG90IDIgY2hlYXQgc2hlZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZikKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBCQVNJQyBHUkFNTUFSCmdncGxvdDIgaXMgYmFzZWQgb24gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MsIHRoZSBpZGVhIHRoYXQgeW91IGNhbiBidWlsZCBldmVyeSBncmFwaCBmcm9tIHRoZSBzYW1lIGNvbXBvbmVudHM6ICoqYSBkYXRhIHNldCoqIAorIAoqKmEgY29vcmRpbmF0ZSBzeXN0ZW0qKiAKKyAKKiphbmQgZ2VvbXPigJR2aXN1YWwgbWFya3MgdGhhdCByZXByZXNlbnQgZGF0YSBwb2ludHMqKgoKIVtHcmFtbWFyIG9mIEdyYXBoaWNzXShodHRwczovL2p1bGVzMzIuZ2l0aHViLmlvL3ItZm9yLWV4Y2VsLXVzZXJzL2ltZy9yc3R1ZGlvLWNoZWF0c2hlZXQtZ2dwbG90LnBuZykKCgojIyBVc2UgQnVpbHQtaW4gRGF0YXNldHMKTGV0J3MgdXNlIHRoZSBidWlsdC1pbiBjYXJzIGRhdGEgc2V0CgpgYGB7cn0KcHJpbnQoY2FycykKYGBgCgpgYGB7ciBHZW5lcmF0aW5nIEVtcHR5IFBsb3R9CmNhcnMgJT4lIGdncGxvdCgpICMgVGhpcyBvbmx5IHNwZWNpZmllcyBhIGRhdGEgc2V0IGFuZCBhIGNvb3JkaW5hdGUgc3lzdGVtIGFuZCB0aGVyZWZvcmUgYW4gZW1wdHkgcGxvdApgYGAKCiMjIGdlb21fcG9pbnQoKSBmdW5jdGlvbgpJdCdzIGVhc3kgdG8gYWRkIGdlb21ldHJ5IGxheWVyIHRvIHRoZSBiYXNlIGNvLW9yZGluYXRlXApMZXQncyBhZGQgYSBsYXllciBvZiBkYXRhIHBvaW50cy4gXApZZXMsIHlvdSBjYW4gYWRkIGxheWVyIGJ5IHVzaW5nIHRoZSArIG9wZXJhdG9yCkxldCdzIHVzZSBwb2ludCAobmFtZWx5ICoqZ2VvbV9wb2ludCgpKiopLgpJbiAyRCBjby1vcmRpbmF0ZSwgYSBwb2ludCBpcyBkZXNjcmliZSBieSBpdHMgeCBhbmQgeSB2YWx1ZS4KCldlIG5lZWQgdG8gcHJvdmlkZSBhIG1hcHBpbmcgdGhhdCBzcGVjaWZpZXMgdGhlIGRhdGEgY29sdW1ucycgbmFtZSB0byBtYXAgdG8gdGhlIHggYW5kIHkgdmFsdWUgb2YgYSBwb2ludAoKVGhhdCBtYXBwaW5nIGlzIGRlZmluZWQgYnkgYW4gYWVzdGhldGljcyBmdW5jdGlvblwKKiphZXMoKSoqCgpTY2F0dGVycGxvdCBpcyB1c2VmdWwgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb24gb2YgdHdvIHZhcmlhYmxlLgoKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkKYGBgCgojIyBVc2UgZ2VvbV9saW5lKCkgdG8gcmVwbGFjZSBnZW9tX3BvaW50KCkKZ2VvbV9wb2ludCgpIGFuZCBnZW9tX2xpbmUoKSByZXF1aXJlIHZlcnkgc2ltaWxhciBwYXJhbWV0ZXJzLlwKZ2VvbV9saW5lKCkgaXMgc2ltcGx5IGFuIGVuaGFuY2VkIHZpc3VhbGl6YXRpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGNvbm5lY3QgYWxsIHRoZSBwb2ludHMKCmBgYHtyfQpjYXJzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkgIyBqdXN0IGNoYW5nZSBnZW9tX3BvaW50IHRvIGdlb21fbGluZSB3aXRob3V0IGNoYW5nZSBhbnl0aGluZyBlbHNlCmBgYAoKCiMjIFVzZSBnZW9tX3Ntb3RoKCkgdG8gcHJvamVjdCBhIHNtb290aCBsaW5lCmFnYWluIGdlb21fc21vb3RoKCkgYW5kIGdlb21fcG9pbnQoKSByZXF1aXJlIHZlcnkgc2ltaWxhciBwYXJhbWV0ZXJzLlwKZ2VvbV9zbW9vdGgoKSBzbW9vdGhzIG91dCB0aGUgbGluZSBwcm9ncmVzc2lvbgoKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHg9c3BlZWQsIHk9ZGlzdCkpICMganVzdCBjaGFuZ2UgZ2VvbV9wb2ludCB0byBnZW9tX2xpbmUgd2l0aG91dCBjaGFuZ2UgYW55dGhpbmcgZWxzZQpgYGAKCiMjIEFkZGluZyBBZXN0aGV0aWNzIHRvIHlvdXIgUGxvdHMKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSwKICAgICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIsICMgdGhlIGNvbG9yIG9mIGRhdGEgcG9pbnRzCiAgICAgICAgICAgICAjIHNpemUgPSAzLCAjIHRoZSBzaXplIG9mIGRhdGEgcG9pbnQKICAgICAgICAgICAgICMgYWxwaGEgPSAwLjUsICMgdGhlIHRyYW5zcGFyZW5jeSBvZiBkYXRhIHBvaW50cywgbWluPTAsIG1heD0xCiAgICAgICAgICAgICAjIHNoYXBlID0gMCwgIyB0aGUgc2hhcGUgb2YgZGF0YSBwb2ludAogICAgICAgICAgICAgKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQTE9UIFdJVEggT1VSIE9XTiBEQVRBCgojIyBMb2FkaW5nIERhdGE6IGFsbG93YW5jZSAmIGdyYWR1YXRlcwpgYGB7ciByZWFkaW5nIGRhdGEgZmlsZXN9CmFsbG93YW5jZSA9IHJlYWRfY3N2KCIuL2RhdGEvYWxsb3dhbmNlLmNzdiIpCnByaW50KGFsbG93YW5jZSkKYGBgCgoKIyMgQWxsb3dhbmNlIERhdGEgU2V0IGluIFNpbXBsZSBTY2F0dGVycGxvdApgYGB7ciBBbGxvd2FuY2UgU2NhdHRlcnBsb3R9CmFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KAogICAgbWFwcGluZz1hZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpLAogICAgY29sb3IgPSAib3JhbmdlIiwKICAgIHNpemUgPSAzCiAgICApIApgYGAKCgoKCiMjIENvbnRpbnVvdXMgVmFsdWVzIHZzLiBEaXNjcmV0ZSBWYWx1ZXMKQ29udGludW91cyB2YWx1ZXMgcmVmZXIgdG8gbnVtYmVycyB2YWx1ZSB0aGF0IGhhcyB3aWRlIHJhbmdlCkRpc2NyZXRlIHZhbHVlcyByZWZlciB0byBhIGxpbWl0ZWQgbnVtYmVyIG9mIHZhbGlkIHZhbHVlcy4gIEl0IGNhbiBiZSBzdHJpbmcuIEl0IGNhbiBiZSBhIGZldyBkaXN0aW5jdCBudW1iZXJzLgoKV2hlbiB5b3UgcHJvZHVjZSBwbG90cywgcGF5IGF0dGVudGlvbiB0byB3aGF0IHR5cGUgb2YgdmFsdWUgYXJlIHJlcXVpcmVkIGJ5IHRoZSBnZW9tcy4KCkluIG1hbnkgY2FzZSwgeW91IHdpbGwgbmVlZCB0byBjb252ZXJ0IHRoZSBkYXRhIGZpcnN0LgoqKm11dGF0ZSgpKiogZnVuY3Rpb24gYXJlIHF1aXRlIG9mdGVuIHVzZWQgZm9yIHRoYXQuCgpleGFtcGxlOgpgYGB7cn0KYWxsb3dhbmNlID0gYWxsb3dhbmNlICU+JSAKICBtdXRhdGUoQXNzZXNzbWVudF9ZZWFyID0gYXMubnVtZXJpYyhzdWJzdHIoQXNzZXNzbWVudF9ZZWFyLCAxICw0KSkpIApgYGAKCgojIyBTaW1wbGUgTGluZSBQbG90CmBgYHtyIEFsbG93YW5jZSBMaW5lIEdyYXBofQoKIyBUaGUgZm9sbG93aW5nIHN0YXRlbWVudCB3b24ndCBnZW5lcmF0ZSBhIHBsb3QKYWxsb3dhbmNlICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpKQoKIyBGb3IgbGluZSBncmFwaHMsIHRoZSBkYXRhIHBvaW50cyBtdXN0IGJlIGdyb3VwZWQgc28gdGhhdCBpdCBrbm93cyB3aGljaCBwb2ludHMgdG8gY29ubmVjdC4gCiMgSW4gdGhpcyBjYXNlLCBhbGwgcG9pbnRzIHNob3VsZCBiZSBjb25uZWN0ZWQsIHNvIGdyb3VwPTEuIAojIFdoZW4gbW9yZSB2YXJpYWJsZXMgYXJlIHVzZWQgYW5kIG11bHRpcGxlIGxpbmVzIGFyZSBkcmF3biwgdGhlIGdyb3VwaW5nIGZvciBsaW5lcyBpcyB1c3VhbGx5IGRvbmUgYnkgdmFyaWFibGUuCmFsbG93YW5jZSAlPiUgZ2dwbG90KCkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpCmBgYAoKIyMgQWRkaW5nIE11bHRpcGxlIExheWVycyBvZiBHZW9tZXRyeQpgYGB7cn0KYWxsb3dhbmNlICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMsIGdyb3VwPTEsIGNvbG9yPSJPcmFuZ2UiKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYywgZ3JvdXA9MSwgY29sb3I9Ik9yYW5nZSIpKQoKIyBBcyBib3RoIGdlb20gdXNlIHRoZSBzYW1lIGRhdGEgbWFwcGluZywgdGhlIGFib3ZlIHN0YXRlbWVudHMgY2FuIGJlIHNpbXBsaWZpZWQgYXMKYWxsb3dhbmNlICU+JSBnZ3Bsb3QoYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChzaXplPTUpCmBgYAoKIyMgVXNlIGdlb21fc21vb3RoKCkgdG8gc21vb3RoIG91dCB0aGUgbGluZQpgYGB7cn0KYWxsb3dhbmNlICU+JSBnZ3Bsb3QoYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpICsKICBnZW9tX3Ntb290aCgpICsKICBnZW9tX3BvaW50KHNpemU9NSkKYGBgCgoKCiMjIFNhdmUgYSBQbG90OiBnZ3NhdmUoKQpgYGB7cn0KbXkuZmlyc3QucGxvdCA9IGFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KAogICAgbWFwcGluZz1hZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpLAogICAgY29sb3IgPSAib3JhbmdlIiwKICAgIHNpemUgPSAzLAogICAgKSAKCnByaW50KG15LmZpcnN0LnBsb3QpCgpnZ3NhdmUoIi4vb3V0cHV0L215X2ZpcnN0X3Bsb3QucG5nIikgIyBkZWZhdWx0IGltYWdlIHNpemUKZ2dzYXZlKCIuL291dHB1dC9teV9maXJzdF9wbG90X2xhcmdlLnBuZyIsIHdpZHRoPTEwLCBoZWlnaHQ9MTApCgpgYGAKCiMjIEJhciBDaGFydCB3aXRoIGdlb21fY29sKCkKCmBgYHtyfQphbGxvd2FuY2UgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYyksCiAgICAgICAgICAgZmlsbD0idG9tYXRvIikgCmBgYAoKCgoKIyMgSGlzdG9ncmFtIHdpdGggZ2VvbV9iYXIoKQpDb3VudGluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggb2NjdXJyZW5jZSBvZiBvYnNlcnZlZCB2YWx1ZS4KCmBgYHtyfQphbGxvd2FuY2UgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JhcihtYXBwaW5nPWFlcyh4PUJhc2ljKSkgIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDSEFMTEVOR0UKIyMgTXVsdGlwbGUgTGF5ZXJzIG9mIExpbmVzCmFkZCBsaW5lIHBsb3QgZm9yIGNvbG91bW4gb2YgQ2hpbGQKaW4gdGhlIHNhbWUgcGxvdCwgYWRkIGFub3RoZXIgbGluZSBwbG90IGZvciBEZXBlbmRlbnRfUGFyZW50XzYwCmBgYHtyfQphbGxvd2FuY2UgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1DaGlsZCwgZ3JvdXA9MSksIGNvbG9yPSJPcmFuZ2UiKSArIAogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PURlcGVuZGVudF9QYXJlbnRfNjAsIGdyb3VwPTEpLCBjb2xvcj0iQmx1ZSIpIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBXT1JLIFdJVEggTU9SRSBDT01QTEVYIERBVEEKCiMjIExvYWRpbmcgRGF0YTogZ3JhZHVhdGVzLmNzdgpgYGB7ciByZWFkaW5nIGNvbXBsZXggZGF0YX0KZ3JhZHVhdGVzID0gcmVhZF9jc3YoIi4vZGF0YS9ncmFkdWF0ZXMuY3N2IikKcHJpbnQoZ3JhZHVhdGVzKQpgYGAKCiMjIFNpbXBsZSBTY2F0dGVycGxvdApMZXQncyBleHBsb3JlIHRoZSBkYXRhIHdpdGggc29tZSBzaW1wbGUgZ2dwbG90IHBsb3QuIApPdmVyYWxsIHRoZXkgYXJlIG5vdCB2ZXJ5IHVzZWZ1bC4gIEp1c3Qgc29tZSBxdWljayBleHBsb3JhdGlvbi4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ncmFkdWF0ZXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCkpCgpnZ3Bsb3QoZGF0YT1ncmFkdWF0ZXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgc2hhcGU9U2V4KSAjIHVzZSBzaGFwZSB0byBkaWZmZXJlbnRpYXRlIGdyb3VwcwogICAgICAgICAgICAgKQoKZ2dwbG90KGRhdGE9Z3JhZHVhdGVzKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgeT1IZWFkY291bnQsIGNvbG9yPVNleCkgIyB1c2UgY29sb3IgdG8gZGlmZmVyZW50aWF0ZSBncm91cHMKICAgICAgICAgICAgICkKYGBgCgojIyBVc2UgZmlsdGVyKCkgdG8gRXh0cmFjdCBSZXF1aXJlZCBSb3dzCmBgYHtyIHVzZSBmaWx0ZXIoKX0KZ3JhZHVhdGVzICU+JSAKZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBQcm9ncmFtbWVDYXRlZ29yeT09IkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IikgJT4lCmdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgY29sb3I9U2V4KQopCmBgYAoKCiMjIFVzZSBMaW5lIFBsb3QgVG8gRXhwbG9yZSB0aGUgVHJlbmRpbmcgClVzZSBsaW5lIHBsb3QgdG8gZXhwbG9yZSB0aGUgdHJlbmRpbmcgb2YgIkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IiBzdHVkZW50IGhlYWRjb3VudCB0cmVuZGluZwoKCmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpncmFkdWF0ZXMgJTw+JQogIG11dGF0ZShBY2FkZW1pY1llYXI9YXMuZmFjdG9yKEFjYWRlbWljWWVhciksCiAgICAgICAgIFNleD1hcy5mYWN0b3IoU2V4KQogICAgICAgICApICMgY29udmVydCB0aGUgQWNhZGVtaWNZZWFyIGFuZCBTZXggdG8gZmFjdG9yIHR5cGUKCmdyYWR1YXRlcyAlPiUgCmZpbHRlcihMZXZlbE9mU3R1ZHk9PSJVbmRlcmdyYWR1YXRlIiwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCIpICU+JQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgbWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgeT1IZWFkY291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgICBncm91cD1TZXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9U2V4CiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICApCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDSEFMTEVOR0UKIyMgQ29tcGFyaXNvbiB3aXRoIExpbmUgUGxvdHMKVXNlIGxpbmUgcGxvdHMgdG8gY29tcGFyZSBmZW1hbGUgdW5kZXJncmFkdWF0ZSBzdHVkZW50cyBoZWFkY291bnQgdHJlbmRpbmcgaW4gUHJvZ3JhbW1lQ2F0ZWdvcnkgb2YgIkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IiBhbmQgIkVuZ2luZWVyaW5nIGFuZCBUZWNobm9sb2d5IgpVc2UgZmlsdGVyKCkgdG8gZXh0cmFjdCByZXF1aXJlZCByZWNvcmQKWW91IGNhbiB1c2UgbXVsdGlwbGUgZmlsdGVyKCkgY2FsbApVc2UgJiwgfCBvciBtdWx0aXBsZSBjb25kaXRpb25zCgpgYGB7cn0KCmdyYWR1YXRlcyAlPiUgCiAgLiRQcm9ncmFtbWVDYXRlZ29yeSAlPiUgCiAgdW5pcXVlKCkgIyBkaXNwbGF5IHRoZSB1bmlxdWUgbmFtZXMgb2YgUHJvZ3JhbW1lQ2F0ZWdvcnkKCmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBTZXg9PSJGIikgJT4lCiAgZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKICBwcmludCgpICMgVGVzdCBleHRyYWN0aW5nIGFuZCBwcmludGluZyB0aGUgcmVxdWlyZWQgcmVjb3Jkcy4KCmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBTZXg9PSJGIikgJT4lCiAgZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKICBnZ3Bsb3QoCiAgICBhZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgeT1IZWFkY291bnQsCiAgICAgICAgICAgICBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgCiAgICAgICAgICAgICBjb2xvcj1Qcm9ncmFtbWVDYXRlZ29yeQogICAgICAgICAgICAgKQogICAgKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgCiAgCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBDSEFMTEVOR0U6IGxpbmUgcGxvdCBmb3IgaGlib3JfZml4aW5nXzFtCmBgYHtyfQpsaWJyYXJ5KGpzb25saXRlKSAjIGxvYWQgcGFja2FnZQpoa21hLmludGVyYmFuay51cmwgPSAiaHR0cHM6Ly9hcGkuaGttYS5nb3YuaGsvcHVibGljL21hcmtldC1kYXRhLWFuZC1zdGF0aXN0aWNzL2RhaWx5LW1vbmV0YXJ5LXN0YXRpc3RpY3MvZGFpbHktZmlndXJlcy1pbnRlcmJhbmstbGlxdWlkaXR5IgppbnRlcmJhbmsubGlxdWlkaXR5ID0gZnJvbUpTT04oaGttYS5pbnRlcmJhbmsudXJsKQojIHRoZSBhYm92ZSByZXRyaWV2YWwgd2lsbCB0YWtlIGEgd2hpbGUuICBUaGUgc2VydmVyIHJlc3BvbnNlIGlzIHNsb3cuCnN1bW1hcnkoaW50ZXJiYW5rLmxpcXVpZGl0eSkKc3RyKGludGVyYmFuay5saXF1aWRpdHkpCmludGVyYmFuay5saXF1aWRpdHkkcmVzdWx0CnN0cihpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCkKaW50ZXJiYW5rLnJlY29yZHMgPSBpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCRyZWNvcmRzICU+JSBhc190aWJibGUoKQppbnRlcmJhbmsucmVjb3JkcwoKaW50ZXJiYW5rLnJlY29yZHMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgbWFwcGluZz1hZXMoeD1lbmRfb2ZfZGF0ZSwgeT1oaWJvcl9maXhpbmdfMW0sIGdyb3VwPTEpLAogICAgY29sb3I9Im9yYW5nZSIKICAgICAgICAgICAgICkKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBHUk9VUElORyBBTkQgQUdHUkVHQVRJT04KCiMjIFVzaW5nIGdyb3VwX2J5KCkgYW5kIHN1bW1hcmlzZSgpIApgYGB7cn0KZ3JhZHVhdGVzICU+JSBncm91cF9ieShBY2FkZW1pY1llYXIsIExldmVsT2ZTdHVkeSkgJT4lIAogIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAKCmdyYWR1YXRlcyAlPiUgZ3JvdXBfYnkoQWNhZGVtaWNZZWFyLCBMZXZlbE9mU3R1ZHkpICU+JSAKICBzdW1tYXJpc2UoVG90YWxIZWFkY291bnQgPSBzdW0oSGVhZGNvdW50KSkgJT4lIAogIGdncGxvdCgKICAgIGFlcyh4PUFjYWRlbWljWWVhciwgCiAgICAgICAgICAgICB5PVRvdGFsSGVhZGNvdW50LAogICAgICAgICAgICAgZ3JvdXA9TGV2ZWxPZlN0dWR5LCAKICAgICAgICAgICAgIGNvbG9yPUxldmVsT2ZTdHVkeQogICAgICAgICAgICAgKQogICAgKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgCiAgCmBgYAoKIyMgVXNlIG9mIGZpbHRlcigpClVzZSBmaWx0ZXIoKSB0byBrZWVwIG9ubHkgIlRhdWdodCBQb3N0Z3JhZHVhdGUiIFJlY29yZHMKClRoaXMgcGxvdCBpcyBub3QgdmVyeSB1c2VmdWwgd2l0aG91dCBwcmV2aW91c2x5IGFwcGx5aW5nIGZpbHRlcigpIGFuZCBncm91cF9ieSgpIGFuZCBzdW1tYXJpc2UoKQoKYGBge3J9CmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlRhdWdodCBQb3N0Z3JhZHVhdGUiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLHk9SGVhZGNvdW50LCBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgY29sb3I9UHJvZ3JhbW1lQ2F0ZWdvcnkpKQpgYGAKCgojIyBmaWx0ZXIoKSArIGdyb3VwX2J5KCkgKyBzdW1tYXJpc2UoKQpVc2UgZmlsdGVyKCkgdG8gZXh0cmFjdCByZXF1aXJlZCByb3dzClVzZSBncm91cF9ieSgpIGFuZCBzdW1tYXJpc2UoKSB0byBncm91cCBhbmQgYWdncmVhdGUgdG90YWwgaGVhZGNvdXQgZm9yIGJvdGggbWFsZSBhbmQgZmVtYWxlCmBgYHtyIGdncGxvdCBsaW5lfQoKZ3JhZHVhdGVzICU+JSAKICBmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVGF1Z2h0IFBvc3RncmFkdWF0ZSIpICU+JSAKICBncm91cF9ieShBY2FkZW1pY1llYXIsIFByb2dyYW1tZUNhdGVnb3J5KSAlPiUgCiAgc3VtbWFyaXNlKFRvdGFsSGVhZGNvdW50ID0gc3VtKEhlYWRjb3VudCkpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIseT1Ub3RhbEhlYWRjb3VudCwgZ3JvdXA9UHJvZ3JhbW1lQ2F0ZWdvcnksIGNvbG9yPVByb2dyYW1tZUNhdGVnb3J5KSkKCgojIGdyYWR1YXRlcyAlPiUgCiMgICBmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVW5kZXJncmFkdWF0ZSIpICU+JSAKIyAgIGdyb3VwX2J5KEFjYWRlbWljWWVhciwgUHJvZ3JhbW1lQ2F0ZWdvcnkpICU+JSAKIyAgIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAlPiUgCiMgICBnZ3Bsb3QoKSArCiMgICAgIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhcix5PVRvdGFsSGVhZGNvdW50LCBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgY29sb3I9UHJvZ3JhbW1lQ2F0ZWdvcnkpKQogICAgCmBgYAoKCgojIyBnZW9tX2NvbCgpIGZ1bmN0aW9uCgpgYGB7cn0KTGV2ZWxPZlN0dWR5ID0gZ3JhZHVhdGVzICU+JSAuJExldmVsT2ZTdHVkeSAlPiUgdW5pcXVlKCkKUHJvZ3JhbW1lQ2F0ZWdvcnkgPSBncmFkdWF0ZXMgJT4lIC4kUHJvZ3JhbW1lQ2F0ZWdvcnkgJT4lIHVuaXF1ZSgpIApwcmludChMZXZlbE9mU3R1ZHkpCnByaW50KFByb2dyYW1tZUNhdGVnb3J5KQogIApncmFkdWF0ZXMgJT4lIApmaWx0ZXIoUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCIpICU+JSAKZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgZmlsbD1MZXZlbE9mU3R1ZHkpKQoKZ3JhZHVhdGVzICU+JSAKZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iRW5naW5lZXJpbmcgYW5kIFRlY2hub2xvZ3kiKSAlPiUgCmdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgeT1IZWFkY291bnQsIGZpbGw9TGV2ZWxPZlN0dWR5KSkKCmBgYAoKIyMgTW9yZSBBZ2dyZWdhdGlvbiBGdW5jdGlvbnMKQ2VudGVyOiBtZWFuKCksIG1lZGlhbigpClNwcmVhZDogc2QoKSwgSVFSKCksIG1hZCgpClJhbmdlOiBtaW4oKSwgbWF4KCksIHF1YW50aWxlKCkKUG9zaXRpb246IGZpcnN0KCksIGxhc3QoKSwgbnRoKCksCkNvdW50OiBuKCksIG5fZGlzdGluY3QoKQpMb2dpY2FsOiBhbnkoKSwgYWxsKCkKCk1vcmUgaW5mb3JtYXRpb24gYXQKW3N1bW1hcmlzZSgpIGZ1bmN0aW9uXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3N1bW1hcmlzZS5odG1sKQoKCiMjIGdlb21fYmFyKCkgZnVuY3Rpb24KYmFyIGNoYXJ0IGdpdmUgdGhlIGNvdW50aW5nIGZyZXF1ZW5jeSAobnVtYmVyIG9mIHJlY29yZCBpbiB0aGUgZGF0YSBzZXQpCmBgYHtyfQoKZ3JhZHVhdGVzICU+JSAKZ2dwbG90KCkgKwogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyKSkgIyB5b3Ugb25seSBuZWVkIHRvIHByb3ZpZGUgdGhlIHggY29sdW1uCmBgYAoKIyMgYm94IHBsb3QKVGhlIGJveHBsb3QgY29tcGFjdGx5IGRpc3BsYXlzIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlLlxuCkl0IHZpc3VhbGlzZXMgZml2ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgKHRoZSBtZWRpYW4sIHR3byBoaW5nZXMgYW5kIHR3byB3aGlza2VycyksIGFuZCBhbGwgIm91dGx5aW5nIiBwb2ludHMgaW5kaXZpZHVhbGx5LgoKYGBge3J9CmdyYWR1YXRlcyAlPiUgCmdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9U2V4LCB5PUhlYWRjb3VudCkpCgpncmFkdWF0ZXMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmc9YWVzKHg9TGV2ZWxPZlN0dWR5LCB5PUhlYWRjb3VudCkpCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNQUtFIElUIFBSRVRUWQpVc2Ugb2YgdGl0bGUsIGxhYmVsLCBiYWNrZ3JvdW5kIGNvbG9yIGFuZCB0aGVtZXMKCmBgYHtyfQpsZXZlbC5iYXIucGxvdCA9IGdyYWR1YXRlcyAlPiUgCmZpbHRlcihQcm9ncmFtbWVDYXRlZ29yeT09IkVuZ2luZWVyaW5nIGFuZCBUZWNobm9sb2d5IikgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50LCBmaWxsPUxldmVsT2ZTdHVkeSkpCmBgYAoKIyMgUGxvdCBCYWNrZ3JvdW5kCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0ib3JhbmdlIikpICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kCmBgYAoKIyMgUGFuZWwgQmFja2dyb3VuZApgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJvcmFuZ2UiKSkgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kCmBgYAoKIyMgUmVtb3ZlIFBsb3QgYW5kIFBhbmVsIEJhY2tncm91bmQKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCmBgYAoKIyMgTGFiZWwKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICMgTGFiZWwgZm9yIFggYXhpcwpgYGAKCiMjIENoYW5nZSBGaWxsIENvbG9ycwpgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBhbmVsIGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBsb3QgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5IikpICsgIyBzdHlsaW5nIHRoZSBncmlkIGxpbmUgZm9yIHktYXhpcwogIHlsYWIoIk51bWJlciBvZiBTdHVkZW50IikgKyAjIExhYmVsIGZvciBZIGF4aXMKICB4bGFiKCJZZWFyIikgKyAjIExhYmVsIGZvciBYIGF4aXMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygicHVycGxlIiwgIm9yYW5nZSIsICJibHVlIiwgInRvbWF0byIpKSAjIHVzZSBjKCkgZnVuY3Rpb24gdG8gc3BlY2lmeSBjb2xvciBsaXN0CmBgYAoKIyMgU3R5bGluZyBMZWdlbmRzCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwdXJwbGUiLCAib3JhbmdlIiwgImJsdWUiLCAidG9tYXRvIiksCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQodGl0bGU9IkxldmVsIG9mIFN0dWR5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikKICAgICAgICAgICAgICAgICAgICApICMgbW92ZSBsZWdlbmQgcG9zaXRpb24gdG8gdG9wIGFuZCBsYWJlbCBwb3NpdGlvbiB0byBib3R0b20gCiAgCmBgYAoKIyMgVGl0bGUKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikKYGBgCgojIyBBZGRpbmcgQW5ub3RhdGlvbnMgVGV4dApBZGQgZXh0cmEgdGV4dHMvc2hhcGUgdG8gZW5oYW5jZSB5b3VyIHZpc3VhbGl6YXRpb24KYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsPSJSZWNvcmRcbkhpZ2giLCB4PSIyMDE3LzE4IiwgeT01MzAwKSAjIHlvdSBjYW4gY2hhbmdlIHRleHQgcG9zaXRpb24gdmFsdWUgb2YgeCBhbmQgeSB0byBzZXQgdGhlIHRleHQgcG9zaXRpb24KYGBgCgojIyBBZGRpbmcgUmVmZXJlbmNlIExpbmUKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsPSJSZWNvcmRcbkhpZ2giLCB4PSIyMDE3LzE4IiwgeT01MzAwKSArICMgeW91IGNhbiBjaGFuZ2UgdGV4dCBwb3NpdGlvbiB2YWx1ZSBvZiB4IGFuZCB5IHRvIHNldCB0aGUgdGV4dCBwb3NpdGlvbgogIGdlb21faGxpbmUoeWludGVyY2VwdD0zMjAwKSArICMgYWRkcyBob3Jpem9udGFsIGxpbmUKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAiMjAxNy8xOCIpICMgYWRkcyB2ZXJ0aWNhbCBsaW5lCiAgCmBgYAoKCiMjIFVzaW5nIFRoZW1lcwpgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWVfYncoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX21pbmltYWwoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX2RhcmsoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQpgYGAKCiMjICBNb3JlIDNyZC1wYXJ0eSBUaGVtZXMKSW5zdGFsbCAqKmdndGhlbWVzKiogcGFja2FnZSB0byB1bmxvY2sgd2lkZXIgc2VsZWN0aW9ucyBvZiB0aGVtZXMuCgpgYGB7cn0KaWYgKCFyZXF1aXJlKCJwYWNtYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikgIyBjaGVjayBpZiBwYWNtYW4gYWxyZWFkeSBpbnN0YWxsZWQuIElmIG5vdCwgaW5zdGFsbCBpdC4KcGFjbWFuOjpwX2xvYWQoZ2d0aGVtZXMpCgpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9leGNlbCgpICMgRXhjZWwgVGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV93c2ooKSAjIFdhbGwgU3RyZWV0IEpvdXJuYWwgVGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9lY29ub21pc3QoKSAjIEVjb25vbWlzdCBUaGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICMgV2FsbCBTdHJlZXQgSm91cm5hbCBUaGVtZQoKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIE1PUkUgUkVTT1VSQ0VTIE9OIGdncGxvdDIKIyMgb2ZmaWNpYWwgd2Vic2l0ZQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8iKQpgYGAKCiMjIGV4dGVudHNpb25zCmBgYHtyfQpicm93c2VVUkwoImh0dHBzOi8vZXh0cy5nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvIikKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBNT0RFTElORwoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBUTyBDT05USU5VRQoKIyMgdW5uZXN0KCkKCiMjIENhdGVnb3JpY2FsIFZhcmlhYmxlCgojIyBSZWNvZGluZyBEYXRhCgojIyBTY2FsaW5nCgojIyBUcmFuc2Zvcm1pbmcgT3V0bGllcnMK